Skip to content

feat(*): auto-proxy for eligible hosts#8035

Open
brkalow wants to merge 8 commits intomainfrom
brkalow/auto-proxy-vercel-subdomains
Open

feat(*): auto-proxy for eligible hosts#8035
brkalow wants to merge 8 commits intomainfrom
brkalow/auto-proxy-vercel-subdomains

Conversation

@brkalow
Copy link
Member

@brkalow brkalow commented Mar 11, 2026

Description

This PR implements automatic FAPI proxying for Vercel preview deployments. When an app is deployed to a .vercel.app subdomain without explicit proxy or domain configuration, the SDK automatically routes FAPI requests through the app's own domain via the /__clerk proxy path.

The auto-detection is centralized across all SDKs:

  • Shared utilities (@clerk/shared/proxy): New isVercelPreviewDeploy() helper detects .vercel.app hostnames
  • Client-side (@clerk/clerk-js): Auto-detect in proxyUrl getter when window.location.hostname is .vercel.app
  • Server-side (@clerk/backend): Auto-detect in authenticateContext constructor when request hostname is .vercel.app
  • Next.js middleware (@clerk/nextjs): Auto-enable proxy interception for /__clerk/* requests on .vercel.app hostnames

Guard conditions prevent auto-detection when explicit configuration is provided: proxyUrl, domain, or environment variables NEXT_PUBLIC_CLERK_PROXY_URL / NEXT_PUBLIC_CLERK_DOMAIN.

Test Results

  • @clerk/shared: 981/981 tests passed
  • @clerk/backend: 1114/1114 tests passed
  • @clerk/clerk-js: 624/624 tests passed
  • @clerk/nextjs: 362 tests passed (49 pre-existing failures unrelated to this change)

Build verification: All packages build cleanly with no type errors.

Checklist

  • pnpm test runs as expected
  • pnpm build runs as expected
  • Tests added for new functionality

Type of change

  • 🌟 New feature

Summary by CodeRabbit

  • New Features

    • Automatic proxy detection for Vercel-style hosts: when no explicit proxy or domain is provided, the frontend API routing can auto-configure a relative proxy for eligible deployments; explicit proxy or domain settings take precedence.
  • Bug Fixes

    • Improved handling of relative proxy URLs in non-browser/server contexts and script-host resolution fallback.
  • Tests

    • Added comprehensive tests validating auto-detection, precedence rules, environment-based behavior, and various host scenarios.

@vercel
Copy link

vercel bot commented Mar 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Mar 20, 2026 9:15pm

Request Review

@changeset-bot
Copy link

changeset-bot bot commented Mar 11, 2026

🦋 Changeset detected

Latest commit: c99aba2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 21 packages
Name Type
@clerk/backend Patch
@clerk/clerk-js Patch
@clerk/nextjs Patch
@clerk/shared Patch
@clerk/agent-toolkit Patch
@clerk/astro Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/hono Patch
@clerk/nuxt Patch
@clerk/react-router Patch
@clerk/tanstack-react-start Patch
@clerk/testing Patch
@clerk/chrome-extension Patch
@clerk/expo Patch
@clerk/expo-passkeys Patch
@clerk/localizations Patch
@clerk/msw Patch
@clerk/react Patch
@clerk/ui Patch
@clerk/vue Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 11, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds exported functions shouldAutoProxy(hostname: string) and getAutoProxyUrlFromEnvironment(...) to the shared package to detect eligible hostnames (e.g., *.vercel.app) and derive a relative proxy path (/__clerk). Integrates this auto-proxy logic into backend AuthenticateContext, clerk-js core, Next.js middleware, and environment-merging utilities so that when neither proxyUrl nor domain is provided, the proxy may be set to the current origin + /__clerk for eligible environments. Adds tests across packages and a changeset entry.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(*): auto-proxy for eligible hosts' accurately describes the main feature being implemented across the codebase - automatic proxy detection for eligible hostnames (specifically .vercel.app subdomains).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can validate your CodeRabbit configuration file in your editor.

If your editor has YAML language server, you can enable auto-completion and validation by adding # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json at the top of your CodeRabbit configuration file.

…omains

Add automatic FAPI proxying detection for Vercel preview deployments across all SDKs. When an app is deployed to a .vercel.app subdomain without explicit proxy/domain configuration, the SDK automatically routes FAPI requests through the app's own domain via /__clerk proxy path.

- Add isVercelPreviewDeploy() helper in @clerk/shared/proxy
- Auto-detect in clerk-js proxyUrl getter for client-side SDK initialization
- Auto-detect in @clerk/backend authenticateContext for server-side auth
- Enable proxy interception in Next.js middleware for /__clerk/* requests on .vercel.app
- Add comprehensive tests for all three layers (shared, backend, clerk-js, nextjs)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 13, 2026

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@8035

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8035

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8035

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8035

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8035

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@8035

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8035

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8035

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8035

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8035

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8035

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8035

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8035

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8035

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8035

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8035

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8035

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8035

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8035

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8035

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8035

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8035

commit: c99aba2

@brkalow brkalow changed the title Auto-proxy for .vercel.app subdomains feat(*): auto-proxy for eligible hosts Mar 13, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.changeset/tiny-badgers-smile.md:
- Line 8: Update the changeset description sentence that currently reads "Add
auto-proxy detection for eligible hosts and generalize the internal helper
naming for future providers." to be specific about which hosts are considered
eligible (e.g., Vercel preview deployments) and to clarify the helper change;
for example, change the description to: "Add auto-proxy detection for Vercel
preview deployments (.vercel.app subdomains) and generalize the internal helper
for future hosting providers." Locate and edit the line containing that
description in the changeset (the sentence beginning "Add auto-proxy detection
for eligible hosts...") and replace it with the clearer, user-facing wording.
- Around line 1-6: The changeset incorrectly marks package bumps as 'patch' but
a new public API (shouldAutoProxy) was added; update the entries so any package
that exports the new function (at least '@clerk/shared', and any packages that
re-export it like '@clerk/backend', '@clerk/clerk-js', '@clerk/nextjs' if
applicable) use 'minor' instead of 'patch' in the
.changeset/tiny-badgers-smile.md file; ensure the header lines for those package
entries read 'minor' to reflect the new public API bump while keeping other
metadata unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0cecde51-9659-43c2-8677-37774a033a71

📥 Commits

Reviewing files that changed from the base of the PR and between dbc580e and 2d729d2.

📒 Files selected for processing (1)
  • .changeset/tiny-badgers-smile.md

Comment on lines +1 to +6
---
'@clerk/backend': patch
'@clerk/clerk-js': patch
'@clerk/nextjs': patch
'@clerk/shared': patch
---
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if shouldAutoProxy is a public export

# Search for shouldAutoProxy exports in the shared package
rg -n "export.*shouldAutoProxy" packages/shared/

Repository: clerk/javascript

Length of output: 152


Update version bumps from patch to minor to reflect the new public API export.

The changeset marks all packages for patch bumps, but the PR introduces a new public API function shouldAutoProxy exported from @clerk/shared. Per semantic versioning standards, new public APIs require minor version bumps, not patch (which is reserved for bug fixes). Update the changeset accordingly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.changeset/tiny-badgers-smile.md around lines 1 - 6, The changeset
incorrectly marks package bumps as 'patch' but a new public API
(shouldAutoProxy) was added; update the entries so any package that exports the
new function (at least '@clerk/shared', and any packages that re-export it like
'@clerk/backend', '@clerk/clerk-js', '@clerk/nextjs' if applicable) use 'minor'
instead of 'patch' in the .changeset/tiny-badgers-smile.md file; ensure the
header lines for those package entries read 'minor' to reflect the new public
API bump while keeping other metadata unchanged.

@brkalow
Copy link
Member Author

brkalow commented Mar 20, 2026

!snapshot

@github-actions

This comment has been minimized.

@brkalow
Copy link
Member Author

brkalow commented Mar 20, 2026

!snapshot

@github-actions

This comment has been minimized.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/shared/src/proxy.ts`:
- Around line 45-47: The normalizeHostname function can throw when called with a
malformed URL (e.g., VERCEL_PROJECT_PRODUCTION_URL); wrap the URL parsing in a
try-catch inside normalizeHostname (the function shown using new
URL(...).hostname) and return an empty string on error so config resolution
doesn't crash; ensure callers that rely on normalizeHostname (where
VERCEL_PROJECT_PRODUCTION_URL is passed) can handle an empty string result
gracefully.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2ad9a329-ea13-4ffe-86f9-f7dec3b163e7

📥 Commits

Reviewing files that changed from the base of the PR and between 2d729d2 and 72d6bdc.

📒 Files selected for processing (5)
  • .changeset/tiny-badgers-smile.md
  • packages/nextjs/src/utils/__tests__/mergeNextClerkPropsWithEnv.test.ts
  • packages/nextjs/src/utils/mergeNextClerkPropsWithEnv.ts
  • packages/shared/src/__tests__/proxy.spec.ts
  • packages/shared/src/proxy.ts

Comment on lines +45 to +47
function normalizeHostname(hostnameOrUrl: string): string {
if (hostnameOrUrl.startsWith('http://') || hostnameOrUrl.startsWith('https://')) {
return new URL(hostnameOrUrl).hostname;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify call path and URL constructor throw behavior
rg -n "normalizeHostname|getAutoProxyUrlFromEnvironment|VERCEL_PROJECT_PRODUCTION_URL" \
  packages/shared/src/proxy.ts packages/nextjs/src/utils/mergeNextClerkPropsWithEnv.ts

node - <<'NODE'
const samples = ['https://', 'https://?x=1', 'https:///foo', 'https://myapp.vercel.app'];
for (const s of samples) {
  try {
    console.log(`${s} => hostname="${new URL(s).hostname}"`);
  } catch (e) {
    console.log(`${s} => THROWS (${e.name})`);
  }
}
NODE

Repository: clerk/javascript

Length of output: 853


🏁 Script executed:

#!/bin/bash
# Get full function context at line 45-47 and surrounding lines
echo "=== normalizeHostname function context (lines 41-55) ==="
sed -n '41,55p' packages/shared/src/proxy.ts | cat -n

echo -e "\n=== getAutoProxyUrlFromEnvironment call context (lines 70-80) ==="
sed -n '70,80p' packages/shared/src/proxy.ts | cat -n

# Search for tests related to proxy.ts or normalizeHostname
echo -e "\n=== Tests for proxy functions ==="
find . -name "*.test.ts" -o -name "*.spec.ts" | xargs grep -l "normalizeHostname\|getAutoProxyUrlFromEnvironment\|proxy" 2>/dev/null | head -10

Repository: clerk/javascript

Length of output: 1707


🏁 Script executed:

#!/bin/bash
# Check if there are tests covering error cases for these functions
echo "=== Searching for test files related to proxy ==="
fd -e "test.ts" -e "spec.ts" | xargs grep -l "normalizeHostname\|getAutoProxyUrlFromEnvironment" 2>/dev/null

# Also check the test directory structure
echo -e "\n=== Checking __tests__ and test directory structure ==="
find packages/shared -type d \( -name "__tests__" -o -name "tests" -o -name "test" \) | head -5

Repository: clerk/javascript

Length of output: 410


🏁 Script executed:

#!/bin/bash
# Read the existing proxy test file to see current coverage
echo "=== Existing proxy tests ==="
cat packages/shared/src/__tests__/proxy.spec.ts

Repository: clerk/javascript

Length of output: 5211


Handle malformed VERCEL_PROJECT_PRODUCTION_URL in hostname normalization to prevent runtime crashes.

At line 76, normalizeHostname() is called with an environment variable without error handling. If VERCEL_PROJECT_PRODUCTION_URL is malformed (e.g., https:// or https://?x=1), new URL() throws a TypeError, crashing config resolution instead of gracefully returning an empty string. Add try-catch to safely handle this:

Proposed fix
function normalizeHostname(hostnameOrUrl: string): string {
  if (hostnameOrUrl.startsWith('http://') || hostnameOrUrl.startsWith('https://')) {
-   return new URL(hostnameOrUrl).hostname;
+   try {
+     return new URL(hostnameOrUrl).hostname;
+   } catch {
+     return '';
+   }
  }

  return hostnameOrUrl.split('/')[0] || '';
}

Also applies to: 76-77

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/shared/src/proxy.ts` around lines 45 - 47, The normalizeHostname
function can throw when called with a malformed URL (e.g.,
VERCEL_PROJECT_PRODUCTION_URL); wrap the URL parsing in a try-catch inside
normalizeHostname (the function shown using new URL(...).hostname) and return an
empty string on error so config resolution doesn't crash; ensure callers that
rely on normalizeHostname (where VERCEL_PROJECT_PRODUCTION_URL is passed) can
handle an empty string result gracefully.

@brkalow
Copy link
Member Author

brkalow commented Mar 20, 2026

!snapshot

@github-actions

This comment has been minimized.

@brkalow
Copy link
Member Author

brkalow commented Mar 20, 2026

!snapshot

@github-actions

This comment has been minimized.

@brkalow
Copy link
Member Author

brkalow commented Mar 20, 2026

!snapshot

@github-actions

This comment has been minimized.

fetch() auto-decompresses response bodies but may leave Content-Encoding
and Content-Length headers intact, causing ERR_CONTENT_DECODING_FAILED
in the browser. Fix with two layers:

1. Request `Accept-Encoding: identity` upstream to avoid a double
   compression pass (FAPI → fetch decompresses → edge re-compresses)
2. Defensively strip Content-Encoding and Content-Length from responses
   since servers may ignore the identity hint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@brkalow
Copy link
Member Author

brkalow commented Mar 20, 2026

!snapshot

@github-actions
Copy link
Contributor

Hey @brkalow - the snapshot version command generated the following package versions:

Package Version
@clerk/agent-toolkit 0.3.5-snapshot.v20260320211535
@clerk/astro 3.0.5-snapshot.v20260320211535
@clerk/backend 3.2.1-snapshot.v20260320211535
@clerk/chrome-extension 3.1.3-snapshot.v20260320211535
@clerk/clerk-js 6.3.1-snapshot.v20260320211535
@clerk/dev-cli 0.1.1-snapshot.v20260320211535
@clerk/expo 3.1.3-snapshot.v20260320211535
@clerk/expo-passkeys 1.0.5-snapshot.v20260320211535
@clerk/express 2.0.5-snapshot.v20260320211535
@clerk/fastify 3.1.3-snapshot.v20260320211535
@clerk/hono 0.1.3-snapshot.v20260320211535
@clerk/localizations 4.2.2-snapshot.v20260320211535
@clerk/msw 0.0.5-snapshot.v20260320211535
@clerk/nextjs 7.0.5-snapshot.v20260320211535
@clerk/nuxt 2.0.5-snapshot.v20260320211535
@clerk/react 6.1.1-snapshot.v20260320211535
@clerk/react-router 3.0.5-snapshot.v20260320211535
@clerk/shared 4.3.1-snapshot.v20260320211535
@clerk/tanstack-react-start 1.0.5-snapshot.v20260320211535
@clerk/testing 2.0.5-snapshot.v20260320211535
@clerk/ui 1.2.2-snapshot.v20260320211535
@clerk/upgrade 2.0.3-snapshot.v20260320211535
@clerk/vue 2.0.5-snapshot.v20260320211535

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/agent-toolkit

npm i @clerk/agent-toolkit@0.3.5-snapshot.v20260320211535 --save-exact

@clerk/astro

npm i @clerk/astro@3.0.5-snapshot.v20260320211535 --save-exact

@clerk/backend

npm i @clerk/backend@3.2.1-snapshot.v20260320211535 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.1.3-snapshot.v20260320211535 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.3.1-snapshot.v20260320211535 --save-exact

@clerk/dev-cli

npm i @clerk/dev-cli@0.1.1-snapshot.v20260320211535 --save-exact

@clerk/expo

npm i @clerk/expo@3.1.3-snapshot.v20260320211535 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.0.5-snapshot.v20260320211535 --save-exact

@clerk/express

npm i @clerk/express@2.0.5-snapshot.v20260320211535 --save-exact

@clerk/fastify

npm i @clerk/fastify@3.1.3-snapshot.v20260320211535 --save-exact

@clerk/hono

npm i @clerk/hono@0.1.3-snapshot.v20260320211535 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.2.2-snapshot.v20260320211535 --save-exact

@clerk/msw

npm i @clerk/msw@0.0.5-snapshot.v20260320211535 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.0.5-snapshot.v20260320211535 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.0.5-snapshot.v20260320211535 --save-exact

@clerk/react

npm i @clerk/react@6.1.1-snapshot.v20260320211535 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.0.5-snapshot.v20260320211535 --save-exact

@clerk/shared

npm i @clerk/shared@4.3.1-snapshot.v20260320211535 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.0.5-snapshot.v20260320211535 --save-exact

@clerk/testing

npm i @clerk/testing@2.0.5-snapshot.v20260320211535 --save-exact

@clerk/ui

npm i @clerk/ui@1.2.2-snapshot.v20260320211535 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.3-snapshot.v20260320211535 --save-exact

@clerk/vue

npm i @clerk/vue@2.0.5-snapshot.v20260320211535 --save-exact

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant